<script setup>
import { ref, onMounted, nextTick } from 'vue'

defineProps({
  msg: String,
})

const canvasRef = ref(null)
const imgPath = ref('/src/assets/id-card.jpeg')
const isProcessing = ref(false)
const selectedPoints = ref([])
const imageElement = ref(null)
const canvas = ref(null)
const ctx = ref(null)
const opencvReady = ref(false)

const handleFileChange = (e) => {
  const file = e.target.files[0]
  if (file) {
    imgPath.value = URL.createObjectURL(file)
    selectedPoints.value = []
    nextTick(() => {
      initCanvas()
    })
  }
}

const initCanvas = () => {
  if (!canvasRef.value) return
  
  canvas.value = canvasRef.value
  ctx.value = canvas.value.getContext('2d')
  
  // 创建图片元素
  imageElement.value = new Image()
  imageElement.value.onload = () => {
    // 设置canvas尺寸
    canvas.value.width = imageElement.value.width
    canvas.value.height = imageElement.value.height
    
    // 绘制图片
    ctx.value.drawImage(imageElement.value, 0, 0)
    
    // 添加点击事件
    canvas.value.onclick = handleCanvasClick
  }
  imageElement.value.src = imgPath.value
}

const handleCanvasClick = (e) => {
  if (selectedPoints.value.length >= 4) return

  const rect = canvas.value.getBoundingClientRect()
  const scaleX = canvas.value.width / rect.width
  const scaleY = canvas.value.height / rect.height
  const x = (e.clientX - rect.left) * scaleX
  const y = (e.clientY - rect.top) * scaleY

  selectedPoints.value.push({ x, y })
  redrawCanvas()
}

const redrawCanvas = () => {
  if (!ctx.value || !imageElement.value) return
  
  // 清除画布并重新绘制图片
  ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height)
  ctx.value.drawImage(imageElement.value, 0, 0)
  
  // 重新绘制所有选中的点
  selectedPoints.value.forEach((point, index) => {
    // 绘制选中的点
    ctx.value.fillStyle = 'red'
    ctx.value.beginPath()
    ctx.value.arc(point.x, point.y, 5, 0, 2 * Math.PI)
    ctx.value.fill()
    
    // 绘制点的编号
    ctx.value.fillStyle = 'white'
    ctx.value.font = '16px Arial'
    ctx.value.fillText(index + 1, point.x + 10, point.y - 10)
  })
  
  // 如果选择了4个点，绘制连线
  if (selectedPoints.value.length === 4) {
    drawPolygon()
  }
}

const drawPolygon = () => {
  ctx.value.strokeStyle = 'red'
  ctx.value.lineWidth = 2
  ctx.value.beginPath()
  ctx.value.moveTo(selectedPoints.value[0].x, selectedPoints.value[0].y)
  
  for (let i = 1; i < selectedPoints.value.length; i++) {
    ctx.value.lineTo(selectedPoints.value[i].x, selectedPoints.value[i].y)
  }
  ctx.value.closePath()
  ctx.value.stroke()
}

const performPerspectiveTransform = () => {
  if (selectedPoints.value.length !== 4) {
    alert('请先选择4个点')
    return
  }
  
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  
  isProcessing.value = true
  
  try {
    // 读取图片
    const src = window.cv.imread(canvas.value)
    const dst = new window.cv.Mat()
    
    // 准备源点和目标点
    const srcPoints = []
    selectedPoints.value.forEach(point => {
      srcPoints.push(point.x, point.y)
    })
    
    // 计算目标尺寸（基于原始图片尺寸）
    const width = imageElement.value.width
    const height = imageElement.value.height
    
    // 目标点（矩形）
    const dstPoints = [0, 0, width, 0, width, height, 0, height]
    
    // 创建变换矩阵
    const srcTri = window.cv.matFromArray(4, 1, window.cv.CV_32FC2, srcPoints)
    const dstTri = window.cv.matFromArray(4, 1, window.cv.CV_32FC2, dstPoints)
    const M = window.cv.getPerspectiveTransform(srcTri, dstTri)
    
    // 执行透视变换
    const dsize = new window.cv.Size(width, height)
    window.cv.warpPerspective(src, dst, M, dsize)
    
    // 显示结果
    window.cv.imshow(canvas.value, dst)
    
    // 清理内存
    src.delete()
    dst.delete()
    srcTri.delete()
    dstTri.delete()
    M.delete()
    
  } catch (error) {
    console.error('透视变换失败:', error)
    alert('透视变换失败，请检查控制台错误信息')
  } finally {
    isProcessing.value = false
  }
}

const resetCanvas = () => {
  selectedPoints.value = []
  redrawCanvas()
}

const removeLastPoint = () => {
  if (selectedPoints.value.length > 0) {
    selectedPoints.value.pop()
    redrawCanvas()
  }
}

const detectMaxRect = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;

  // 1. 读取图片
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let thresh = new window.cv.Mat();
  let edged = new window.cv.Mat();
  let dilated = new window.cv.Mat();
  let contours = new window.cv.MatVector();
  let hierarchy = new window.cv.Mat();
  let approx = new window.cv.Mat();

  try {
    // 2. 灰度化
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    // 3. 全局阈值
    window.cv.threshold(gray, thresh, 120, 255, window.cv.THRESH_BINARY);
    // 4. 边缘检测
    window.cv.Canny(thresh, edged, 30, 120);
    // 5. 膨胀操作
    let M = window.cv.Mat.ones(3, 3, window.cv.CV_8U);
    window.cv.dilate(edged, dilated, M);
    M.delete();
    // 6. 轮廓检测
    window.cv.findContours(dilated, contours, hierarchy, window.cv.RETR_LIST, window.cv.CHAIN_APPROX_SIMPLE);

    let maxArea = 0;
    let maxRect = null;
    let quadCount = 0;
    let allAreas = [];
    let maxPoints = [];
    for (let i = 0; i < contours.size(); ++i) {
      let cnt = contours.get(i);
      window.cv.approxPolyDP(cnt, approx, 0.02 * window.cv.arcLength(cnt, true), true);
      if (approx.rows === 4 && window.cv.isContourConvex(approx)) {
        let area = window.cv.contourArea(approx);
        allAreas.push(area);
        quadCount++;
        if (area > maxArea) {
          maxArea = area;
          if (maxRect) maxRect.delete();
          maxRect = approx.clone();
          maxPoints = [];
          for (let j = 0; j < 4; j++) {
            maxPoints.push({
              x: maxRect.intPtr(j, 0)[0],
              y: maxRect.intPtr(j, 0)[1],
            });
          }
        }
      }
      cnt.delete();
    }
    console.log('检测到四边形数量:', quadCount, '最大面积:', maxArea, '最大点:', maxPoints, '所有面积:', allAreas);

    if (maxRect) {
      // 排序四个点（左上、右上、右下、左下）
      maxPoints = sortPoints(maxPoints);
      selectedPoints.value = maxPoints;
      redrawCanvas();
      // 自动执行透视矫正
      performPerspectiveTransform();
      maxRect.delete();
    } else {
      alert('未检测到四边形区域');
    }
  } catch (err) {
    console.error('自动检测矩形失败:', err);
    alert('自动检测矩形失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    thresh.delete();
    edged.delete();
    dilated.delete();
    contours.delete();
    hierarchy.delete();
    approx.delete();
  }
}

// 四点排序：左上、右上、右下、左下
function sortPoints(pts) {
  // 计算质心
  const cx = pts.reduce((sum, p) => sum + p.x, 0) / 4;
  const cy = pts.reduce((sum, p) => sum + p.y, 0) / 4;
  // 按极角排序
  return pts.slice().sort((a, b) => Math.atan2(a.y - cy, a.x - cx) - Math.atan2(b.y - cy, b.x - cx));
}

// 灰度+高斯模糊
const showStep1GrayBlur = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;

  // 1. 读取图片
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let blurred = new window.cv.Mat();
  try {
    // 灰度化
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    // 高斯模糊
    window.cv.GaussianBlur(gray, blurred, new window.cv.Size(5, 5), 0);
    // 显示到canvas
    window.cv.imshow(canvas.value, blurred);
    console.log('[Step1] 灰度+高斯模糊完成');
  } catch (err) {
    console.error('[Step1] 灰度化/高斯模糊失败:', err);
    alert('灰度化/高斯模糊失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    blurred.delete();
  }
}

// 直方图均衡化
const showStepHistEq = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let histEq = new window.cv.Mat();
  try {
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    window.cv.equalizeHist(gray, histEq);
    window.cv.imshow(canvas.value, histEq);
    console.log('[Step3] 直方图均衡化完成');
  } catch (err) {
    console.error('[Step3] 直方图均衡化失败:', err);
    alert('直方图均衡化失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    histEq.delete();
  }
}

// 二值化+膨胀
const showStepThreshDilate = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let thresh = new window.cv.Mat();
  let dilated = new window.cv.Mat();
  try {
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    window.cv.threshold(gray, thresh, 120, 255, window.cv.THRESH_BINARY);
    let M = window.cv.Mat.ones(5, 5, window.cv.CV_8U);
    window.cv.dilate(thresh, dilated, M);
    M.delete();
    window.cv.imshow(canvas.value, dilated);
    console.log('[Step4] 二值化+膨胀完成');
  } catch (err) {
    console.error('[Step4] 二值化+膨胀失败:', err);
    alert('二值化+膨胀失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    thresh.delete();
    dilated.delete();
  }
}

// 第三步：Canny边缘检测
const showStep2Canny = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;

  // 1. 读取图片
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let blurred = new window.cv.Mat();
  let edged = new window.cv.Mat();
  try {
    // 灰度化
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    // 高斯模糊
    window.cv.GaussianBlur(gray, blurred, new window.cv.Size(5, 5), 0);
    // Canny边缘检测
    window.cv.Canny(blurred, edged, 50, 150);
    // 显示到canvas
    window.cv.imshow(canvas.value, edged);
    console.log('[Step2] Canny边缘检测完成');
  } catch (err) {
    console.error('[Step2] Canny边缘检测失败:', err);
    alert('Canny边缘检测失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    blurred.delete();
    edged.delete();
  }
}

// 低阈值Canny
const showStepLowCanny = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let blurred = new window.cv.Mat();
  let edged = new window.cv.Mat();
  try {
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    window.cv.GaussianBlur(gray, blurred, new window.cv.Size(5, 5), 0);
    window.cv.Canny(blurred, edged, 20, 80);
    window.cv.imshow(canvas.value, edged);
    console.log('[Step5] 低阈值Canny完成');
  } catch (err) {
    console.error('[Step5] 低阈值Canny失败:', err);
    alert('低阈值Canny失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    blurred.delete();
    edged.delete();
  }
}

// 霍夫直线检测
const showStepHoughLines = () => {
  if (!opencvReady.value) {
    alert('OpenCV.js 尚未加载完成，请稍后再试')
    return
  }
  if (!canvas.value || !imageElement.value) return;
  const src = window.cv.imread(canvas.value);
  let gray = new window.cv.Mat();
  let blurred = new window.cv.Mat();
  let edges = new window.cv.Mat();
  let color = new window.cv.Mat();
  try {
    // 灰度+高斯模糊
    window.cv.cvtColor(src, gray, window.cv.COLOR_RGBA2GRAY, 0);
    window.cv.GaussianBlur(gray, blurred, new window.cv.Size(5, 5), 0);
    // Canny边缘
    window.cv.Canny(blurred, edges, 50, 150);
    // 转为彩色图用于画线
    window.cv.cvtColor(edges, color, window.cv.COLOR_GRAY2RGBA, 0);
    // 霍夫直线检测
    let lines = new window.cv.Mat();
    window.cv.HoughLinesP(edges, lines, 1, Math.PI / 180, 80, 80, 10);
    // 绘制所有直线
    for (let i = 0; i < lines.rows; ++i) {
      let [x1, y1, x2, y2] = lines.intPtr(i);
      window.cv.line(color, new window.cv.Point(x1, y1), new window.cv.Point(x2, y2), [255, 0, 0, 255], 2);
    }
    window.cv.imshow(canvas.value, color);
    console.log('[Step6] 霍夫直线检测完成，检测到直线数:', lines.rows);
    lines.delete();
  } catch (err) {
    console.error('[Step6] 霍夫直线检测失败:', err);
    alert('霍夫直线检测失败，请检查控制台错误信息');
  } finally {
    src.delete();
    gray.delete();
    blurred.delete();
    edges.delete();
    color.delete();
  }
}

onMounted(() => {
  console.log('cv', window.cv)
  
  // 检查OpenCV加载状态
  const checkOpenCV = () => {
    if (window.cv && window.cvReady) {
      opencvReady.value = true
      console.log('OpenCV.js 已加载完成')
    } else {
      setTimeout(checkOpenCV, 100)
    }
  }
  checkOpenCV()
  
  // 初始化默认图片
  nextTick(() => {
    initCanvas()
  })
})
</script>

<template>
  <div class="perspective-correction">
    <h1>{{ msg }}</h1>
    
    <div class="opencv-status" :class="{ ready: opencvReady }">
      OpenCV.js 状态: {{ opencvReady ? '已加载' : '加载中...' }}
    </div>
    
    <div class="controls">
      <input type="file" @change="handleFileChange" accept="image/*" />
      <button @click="showStep1GrayBlur" :disabled="isProcessing || !opencvReady">显示灰度+高斯模糊</button>
      <button @click="showStepHistEq" :disabled="isProcessing || !opencvReady">显示直方图均衡化</button>
      <button @click="showStepThreshDilate" :disabled="isProcessing || !opencvReady">显示二值化+膨胀</button>
      <button @click="showStep2Canny" :disabled="isProcessing || !opencvReady">显示Canny边缘</button>
      <button @click="showStepLowCanny" :disabled="isProcessing || !opencvReady">显示低阈值Canny</button>
      <button @click="showStepHoughLines" :disabled="isProcessing || !opencvReady">显示霍夫直线检测</button>
      <!-- 其余按钮注释或移除，后续逐步梳理 -->
      <!--
      <button @click="showStep2Threshold" :disabled="isProcessing || !opencvReady">显示二值化</button>
      <button @click="showStep2AdaptiveThreshold" :disabled="isProcessing || !opencvReady">显示自适应阈值</button>
      <button @click="performPerspectiveTransform" :disabled="selectedPoints.length !== 4 || isProcessing">{{ isProcessing ? '处理中...' : '执行透视矫正' }}</button>
      <button @click="resetCanvas">重置</button>
      <button @click="removeLastPoint" :disabled="selectedPoints.length === 0">删除最后一点</button>
      <button @click="detectMaxRect" :disabled="isProcessing || !opencvReady">自动检测最大矩形</button>
      -->
    </div>
    
    <div class="canvas-container">
      <canvas 
        ref="canvasRef" 
        class="correction-canvas"
        :style="{ cursor: selectedPoints.length < 4 ? 'crosshair' : 'default' }"
      ></canvas>
    </div>
    
          <div class="instructions">
        <h3>使用说明：</h3>
        <ol>
          <li>选择一张图片（或使用默认的身份证图片）</li>
          <li>点击图片上的4个点，按顺序选择（建议从左上角开始顺时针）</li>
          <li>选择完4个点后，点击"执行透视矫正"按钮</li>
          <li>如需删除最后一点，点击"删除最后一点"按钮</li>
          <li>如需重新选择，点击"重置"按钮</li>
        </ol>
        <p>当前已选择: {{ selectedPoints.length }}/4 个点</p>
        <p><strong>提示：</strong>选择点时建议按照以下顺序：左上角 → 右上角 → 右下角 → 左下角</p>
      </div>
  </div>
</template>

<style scoped>
.perspective-correction {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.controls {
  margin: 20px 0;
  display: flex;
  gap: 10px;
  align-items: center;
}

.controls button {
  padding: 8px 16px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.controls button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.controls button:hover:not(:disabled) {
  background-color: #45a049;
}

.canvas-container {
  margin: 20px 0;
  border: 2px solid #ddd;
  display: inline-block;
}

.correction-canvas {
  max-width: 100%;
  height: auto;
  display: block;
}

.instructions {
  margin-top: 20px;
  padding: 15px;
  background-color: #f9f9f9;
  border-radius: 5px;
}

.instructions h3 {
  margin-top: 0;
  color: #333;
}

.instructions ol {
  margin: 10px 0;
  padding-left: 20px;
}

.instructions li {
  margin: 5px 0;
}

.instructions p {
  font-weight: bold;
  color: #666;
}

.opencv-status {
  padding: 10px;
  margin: 10px 0;
  background-color: #fff3cd;
  border: 1px solid #ffeaa7;
  border-radius: 4px;
  color: #856404;
}

.opencv-status.ready {
  background-color: #d4edda;
  border-color: #c3e6cb;
  color: #155724;
}
</style>
